package com.trendytech.tds.common.mybatis;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.apache.ibatis.session.RowBounds;

/**
 * MyBatis物理分页插件
 * 
 */
@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) })
public class PaginationInterceptor implements Interceptor {
	
	protected static Log logger = LogFactory.getLog(PaginationInterceptor.class);
	
	private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
	
	private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
	// 匹配from后的SQL
	private static final Pattern PATTERN_SQL = Pattern.compile(
			"\\s+from\\s+([\\s\\S]*)", Pattern.CASE_INSENSITIVE);
	// 匹配带order by的SQL，用于在查询总数时删除order by，因为sqlserver不支持
	private static final Pattern PATTERN_ORDERBY_SQL = Pattern.compile(
			"\\s+from\\s+([\\s\\S]*)\\s+(order\\s+by\\s+.*)", Pattern.CASE_INSENSITIVE);
	
	private Dialect dialect;

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		StatementHandler statementHandler = (StatementHandler) invocation
				.getTarget();
		MetaObject metaStatementHandler = MetaObject.forObject(
				statementHandler, DEFAULT_OBJECT_FACTORY,
				DEFAULT_OBJECT_WRAPPER_FACTORY);
		RowBounds rowBounds = (RowBounds) metaStatementHandler
				.getValue("delegate.rowBounds");
		if (rowBounds == null || rowBounds == RowBounds.DEFAULT) {
			return invocation.proceed();
		}
		// 分离代理对象链(由于目标类可能被多个拦截器拦截，从而形成多次代理，通过下面的两次循环
		// 可以分离出最原始的的目标类)
		while (metaStatementHandler.hasGetter("h")) {
			Object object = metaStatementHandler.getValue("h");
			metaStatementHandler = MetaObject.forObject(object,
					DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY);
		}
		// 分离最后一个代理对象的目标类
		while (metaStatementHandler.hasGetter("target")) {
			Object object = metaStatementHandler.getValue("target");
			metaStatementHandler = MetaObject.forObject(object,
					DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY);
		}
		// Configuration configuration = (Configuration)
		// metaStatementHandler.getValue("delegate.configuration");
		// MappedStatement mappedStatement = (MappedStatement)
		// metaStatementHandler.getValue("delegate.mappedStatement");
		BoundSql boundSql = (BoundSql) metaStatementHandler
				.getValue("delegate.boundSql");
		Object parameterObject = boundSql.getParameterObject();
		if(parameterObject instanceof IPagination){
			int rowCount = getRowCount(invocation, statementHandler, boundSql);
			((IPagination)parameterObject).setRowCount(rowCount);
		}
		String originalSql = boundSql.getSql();
		String pageSql = dialect.getLimitString(originalSql,
				rowBounds.getOffset(), rowBounds.getLimit());
		metaStatementHandler.setValue("delegate.boundSql.sql", pageSql);
		metaStatementHandler.setValue("delegate.rowBounds.offset",
				RowBounds.NO_ROW_OFFSET);
		metaStatementHandler.setValue("delegate.rowBounds.limit",
				RowBounds.NO_ROW_LIMIT);
		// 将执行权交给下一个拦截器
		return invocation.proceed();
	}

	@Override
	public Object plugin(Object target) {
		if (target instanceof StatementHandler) {
			return Plugin.wrap(target, this);
		} else {
			return target;
		}
	}

	@Override
	public void setProperties(Properties properties) {
		String dialectName = properties.getProperty("dialect").toUpperCase();
		Dialect.Type dialectType = Dialect.Type.valueOf(dialectName);
		switch (dialectType) {
		case ORACLE:
			dialect = new OracleDialect();
			break;
		case MYSQL:
			dialect = new MySQLDialect();
			break;
		default:

			break;
		}
	}

	private int getRowCount(Invocation invocation,
			StatementHandler statementHandler, BoundSql boundSql)
			throws Throwable {
		int rowCount = 0;
		PreparedStatement pst = null;
		ResultSet rs = null;
		String countSql = null;
		String originalSql = boundSql.getSql();
		//如果有order by则去掉
		Matcher mOrderBy = PATTERN_ORDERBY_SQL.matcher(originalSql);
		if (mOrderBy.find()) {
			countSql = " select count(*) from " + mOrderBy.group(1);
		} else {
			Matcher m = PATTERN_SQL.matcher(originalSql);
			if (m.find()) {
				countSql = " select count(*) from " + m.group(1);
			}
		}
		try {
			Connection conn = (Connection) invocation.getArgs()[0];
			pst = conn.prepareStatement(countSql);
			statementHandler.parameterize(pst);
			rs = pst.executeQuery();
			if (rs.next()) {
				rowCount = rs.getInt(1);
			}
		} finally {
			if (rs != null) {
				try {
					rs.close();
				} catch (Exception e) {
				}
			}
			if (pst != null) {
				try {
					pst.close();
				} catch (Exception e) {
				}
			}
		}
		return rowCount;
	}
	
}
